#include "Boss.h"

#include "BossFall.h"
#include "BossIdle.h"
#include "BossJump.h"
#include "BossWalk.h"

//====================================================================================================
// Class Definitions
//====================================================================================================

Boss::Boss()
	: mpCurrentState(NULL)
	, mPosition(0.0f, 0.0f)
	, mVelocity(0.0f, 0.0f)
	, mFacingLeft(false)
{
	memset(mBossStates, 0, sizeof(BossState*) * eAS_Max);
}

//----------------------------------------------------------------------------------------------------

Boss::~Boss()
{
	// Empty
}

//----------------------------------------------------------------------------------------------------

void Boss::Load(int level)
{
	// Clear everything
	Unload();

	// Create character states
	mBossStates[eAS_Idle] = new BossIdle(this);
	mBossStates[eAS_Walk] = new BossWalk(this);
	mBossStates[eAS_Jump] = new BossJump(this);
	mBossStates[eAS_Fall] = new BossFall(this);

	// Load state resources
	for (int i = 0; i < bAS_Max; ++i)
		mBossStates[i]->Load(level);

	// Set default state
	ChangeState(bAS_Fall);
}

//----------------------------------------------------------------------------------------------------

void Boss::Unload()
{
	for (int i = 0; i < bAS_Max; ++i)
	{
		if (mBossStates[i] != NULL)
		{
			mBossStates[i]->Unload();
			delete mBossStates[i];
			mBossStates[i] = NULL;
		}
	}
	mpCurrentState = NULL;
}

//----------------------------------------------------------------------------------------------------

void Boss::Update(float deltaTime, const Map& map)
{
	if ( mActive )
	{
		// Update the current state
		mpCurrentState->Update(deltaTime);

		// Apply gravity
		mVelocity.y += (int)(100.0f * deltaTime);
		mVelocity.y = Min(32.0f, mVelocity.y);

		// Apply movement
		CheckCollision(deltaTime, map);
	}
}

//----------------------------------------------------------------------------------------------------

void Boss::Render(const SVector2& offset)
{
	if ( mActive )
	{
		mpCurrentState->Render(offset);
	}
}

//----------------------------------------------------------------------------------------------------

void Boss::Spawn(const SVector2& pos)
{
	mPosition = pos;
	mVelocity = SVector2(0.0f, 200.0f);
	mActive = true;
};

void Boss::Kill()
{
	mActive = false;
}

void Boss::ChangeState(bAnimationState as)
{
	// Check if we are already in this state
	if (mpCurrentState == mBossStates[as])
	{
		return;
	}

	// Exit the current state
	if (mpCurrentState != NULL)
	{
		mpCurrentState->Exit();
	}

	// Switch to the new state
	mpCurrentState = mBossStates[as];

	// Enter the new state
	if (mpCurrentState != NULL)
	{
		mpCurrentState->Enter();
	}
}

//----------------------------------------------------------------------------------------------------

const char* Boss::GetCurrentState()
{
	const char* name = "Unknown";
	if (mpCurrentState != NULL)
	{
		name = mpCurrentState->GetName();
	}
	return name;
}

//----------------------------------------------------------------------------------------------------

SRect Boss::GetBoundingBox() const
{
	return mpCurrentState->GetBoundingBox();
}

//----------------------------------------------------------------------------------------------------

void Boss::CheckCollision(float deltaTime, const Map& map)
{
	// Check collision
	SRect bb = GetBoundingBox();
	SRect newbb = bb + SVector2(mVelocity.x, 0.0f);
	SRect rightbb = map.GetBoundingBoxFromSegment(newbb.GetRightSegment());
	SRect leftbb = map.GetBoundingBoxFromSegment(newbb.GetLeftSegment());

	// Right collision
	if (mVelocity.x > 0.0f && rightbb.IsValid())
	{
		mPosition.x += static_cast<int>(rightbb.min.x - bb.max.x) - 1.0f;
	}
	// Left collision
	else if (mVelocity.x < 0.0f && leftbb.IsValid())
	{
		mPosition.x += static_cast<int>(leftbb.max.x - bb.min.x) + 1.0f;
	}
	else
	{
		mPosition.x += static_cast<int>(mVelocity.x);
	}

	// Check collision
	newbb = bb + SVector2(0.0f, mVelocity.y);
	SRect bottombb = map.GetBoundingBoxFromSegment(newbb.GetBottomSegment());
	SRect topbb = map.GetBoundingBoxFromSegment(newbb.GetTopSegment());

	// Bottom collision
	if (mVelocity.y > 0.0f && bottombb.IsValid())
	{
		mPosition.y += static_cast<int>(bottombb.min.y - bb.max.y) - 1.0f;
		mVelocity.y = 0.0f;
		if (mpCurrentState == mBossStates[bAS_Fall])
		{
			ChangeState(bAS_Idle);
		}
	}
	// Top collision
	else if (mVelocity.y < 0.0f && topbb.IsValid())
	{
		mPosition.y += static_cast<int>(topbb.max.y - bb.min.y) + 1.0f;
		mVelocity.y = 0.0f;
	}
	else
	{
		mPosition.y += static_cast<int>(mVelocity.y);
		if (mpCurrentState == mBossStates[bAS_Idle] ||
			mpCurrentState == mBossStates[bAS_Walk])
		{
			ChangeState(bAS_Fall);
		}
	}
}